/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.monkey;

import com.intellij.codeInspection.*;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.util.IncorrectOperationException;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RemoveTestConstantInspection extends LocalInspectionTool {

    public String getGroupDisplayName() {
        return "PurpleMonkeyDishwasher";
    }

    public String getDisplayName() {
        return "Rename Test Constant";
    }

    public String getShortName() {
        return "RemoveTestConstant";
    }

    public boolean isEnabledByDefault() {
        return true;
    }

    public ProblemDescriptor[] checkField(PsiField field, InspectionManager manager, boolean isOnTheFly) {
        if (!field.getModifierList().hasModifierProperty("final")) {
            return null;
        }
        PsiExpression initializer = field.getInitializer();
        if (initializer != null) {
            if (checkIsTest(field.getContainingFile())) {
                if (isInvalidConstant(initializer)) {
                    return new ProblemDescriptor[]{manager.createProblemDescriptor(field.getNameIdentifier(), "This constant should be a field",
                            new ConvertConstantToFieldQuickFix(), ProblemHighlightType.GENERIC_ERROR_OR_WARNING)};
                }
            }
        }
        return null;
    }

    private boolean isInvalidConstant(PsiExpression initializer) {
        if (initializer instanceof PsiLiteralExpression || initializer instanceof PsiClassObjectAccessExpression) {
            return false;
        }
        if (initializer instanceof PsiReferenceExpression) {
            PsiReferenceExpression psiReferenceExpression = (PsiReferenceExpression) initializer;
            PsiElement psiElement = psiReferenceExpression.resolve();
            if (psiElement != null && psiElement.getContainingFile() != initializer.getContainingFile()) {
                return false;
            }
        }
        if (initializer instanceof PsiBinaryExpression) {
            PsiBinaryExpression psiBinaryExpression = (PsiBinaryExpression) initializer;
            return isInvalidConstant(psiBinaryExpression.getLOperand()) || isInvalidConstant(psiBinaryExpression.getROperand());
        }
        if (initializer instanceof PsiArrayInitializerExpression) {
            PsiArrayInitializerExpression psiArrayInitializerExpression = (PsiArrayInitializerExpression) initializer;
            PsiExpression[] initializers = psiArrayInitializerExpression.getInitializers();
            for (int i = 0; i < initializers.length; i++) {
                if (isInvalidConstant(initializers[i])) {
                    return true;
                }
            }
            return false;

        }
        return true;
    }

    private boolean checkIsTest(PsiFile containingFile) {
        return containingFile.getName().endsWith("Test.java");
    }

    private class ConvertConstantToFieldQuickFix implements LocalQuickFix {

        public String getName() {
            return "Convert constant to field";
        }

        public void applyFix(Project project, ProblemDescriptor problemdescriptor) {
            PsiField field = (PsiField) problemdescriptor.getPsiElement().getParent();
            try {
                field.getModifierList().setModifierProperty(PsiModifier.STATIC, false);
                field.getModifierList().setModifierProperty(PsiModifier.FINAL, false);
                RenameProcessor renameprocessor = new RenameProcessor(project, field, getNewName(field.getName()),
                        false, false);
                renameprocessor.run();
            } catch (IncorrectOperationException e) {
                throw new RuntimeException(e);
            }
        }

        public boolean startInWriteAction() {
            return true;
        }

        public String getFamilyName() {
            return getName();
        }


        private String getNewName(String oldName) {
            if (oldName.matches("[a-z][A-Za-z0-9]+")) {
                return oldName;
            }
            StringBuffer sb = new StringBuffer();
            Pattern pattern = Pattern.compile("_([a-z0-9])");
            Matcher matcher = pattern.matcher(oldName.toLowerCase());
            while (matcher.find()) {
                String letter = matcher.group(1);
                matcher.appendReplacement(sb, letter.toUpperCase());
            }
            matcher.appendTail(sb);
            return sb.toString();
        }
    }
}
